#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WEBGL
#define LOCAL_LOGIC_SERVICE
#endif

using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GamePlay;
using NetProtocol.POD;
using NetProtocol.Server;
using System;
using System.Collections.Generic;
using System.Linq;
using static NetProtocol.Server.SNetBattleLogic;

namespace IQIGame.Onigao.Logic
{
    public class SBattleLogicModule : Singleton<SBattleLogicModule>, SNetBattleLogic
    {
        private SBattleLogicModule() { }
        public static SNetBattleLogic NetCore => Instance;

#if LOCAL_LOGIC_SERVICE
        public int uniqueBattleLogicFrame
        {
            get
            {
                if (_battleMap.Count == 0)
                {
                    return -1;
                }
                return _battleMap.First((x) => true).Value.frame;
            }
        }
#endif
        private Dictionary<long, Battle> _battleMap = new Dictionary<long, Battle>();
        private List<Battle> _battles = new List<Battle>();
        private Dictionary<long, BattlePlayer> _battlePlayers = new Dictionary<long, BattlePlayer>();
        private Dictionary<ISession, BattlePlayer> _sessionPlayers = new Dictionary<ISession, BattlePlayer>();
        public BehaviacManager behaviacManager { get; private set; }

#if UNITY_EDITOR
        // 编辑器数据展示用
        public Dictionary<long, Battle> battleMap => _battleMap;
#endif

        public int BattleCount => _battles.Count;

#if LOCAL_LOGIC_SERVICE
        /** 获取本地关卡 */
        public Battle GetLocalBattle()
        {
            if (_battles.Count > 1) throw new Exception("not local mode");
            if (_battles.Count == 1)
            {
                Battle battle = _battles[0];
                if (!battle.isLocal) throw new Exception("not local mode");
                return battle;
            }
            return null;
        }
#endif

        public Battle GetBattle(long id)
        {
            this._battleMap.TryGetValue(id, out var rBattle);
            return rBattle;
        }
        public void LogicStart()
        {
            InitModuleManager();
        }

        public void LogicOnUpdate()
        {
            for (int i = 0; i < this._battles.Count; i++)
            {
                this._battles[i].LogicOnUpdate();
            }
        }

        public void LogicStop()
        {
            ShutdownModuleManager();
        }

        #region CS_API 手动实现的客户端请求

        ReadyToStartResult SNetBattleLogic.CS_ReadyToStart(ISession session)
        {
            BattlePlayer player = GetBattlePlayer(session);
            if (player == null)
            {
                return NetCore.SC_ReadyToStartResult(ErrorCode.InvalidPlayer);
            }
            return NetCore.SC_ReadyToStartResult(ErrorCode.SUCCESS).SetDelayCall(() =>
            {
                player.isReadyToStart = true;
                bool isAllReady = player.battle.IsAllPlayersReady();
                if (isAllReady)
                {
                    player.battle.Active();
                    NetCore.SC_NotifyStart_Broadcast(player.battle.AllSessions);
                }
            });
        }

        bool SNetBattleLogic.CS_RequestLeave(ISession session)
        {
            BattlePlayer player = GetBattlePlayer(session);
            if (player == null)
            {
                return false;
            }
            LeaveBattle(player, true);
            return true;
        }

        CastSkillResult SNetBattleLogic.CS_CastSkill(ISession session, BattleSkillPOD battleSkillPod)
        {
            BattlePlayer player = GetBattlePlayer(session);
            if (player == null)
            {
                return NetCore.SC_CastSkillResult(ErrorCode.InvalidPlayer);
            }
            player.battle.CastSkill(battleSkillPod);
            return NetCore.SC_CastSkillResult(ErrorCode.SUCCESS);
        }

        bool SNetBattleLogic.CS_CastComplete(ISession session)
        {
            BattlePlayer player = GetBattlePlayer(session);
            if (player == null)
            {
                return false;
            }
            player.battle.SkillComplete();
            return true;
        }

        AutoBattleResult SNetBattleLogic.CS_AutoBattle(ISession session, BattleAutoPOD openPOD)
        {
            BattlePlayer player = GetBattlePlayer(session);
            if (player == null)
            {
                return NetCore.SC_AutoBattleResult(ErrorCode.InvalidPlayer);
            }
            bool open = player.battle.AutoBattle(player, openPOD);
            return NetCore.SC_AutoBattleResult(ErrorCode.SUCCESS, open);
        }

        bool SNetBattleLogic.CS_Substitute(ISession session, int oldGuid)
        {
            BattlePlayer player = GetBattlePlayer(session);
            if (player == null)
            {
                return false;
            }
            player.battle.Substitute(oldGuid);
            return true;
        }

        bool SNetBattleLogic.CS_SkipBattle(ISession session)
        {
            BattlePlayer player = GetBattlePlayer(session);
            if (player == null)
            {
                return false;
            }
            player.battle.SkipBattle();
            return true;
        }

        #endregion

        public bool CreateBattle(CreateBattlePOD enterPOD, Action<BattleEndData> onBattleEnd)
        {
            var battle = ClassPool.Get<Battle>();
            battle.Init(enterPOD, onBattleEnd);
            this._battleMap.Add(battle.id, battle); // 该抛异常抛异常
            this._battles.Add(battle);
            return true;
        }

        public bool ShutdownBattle(long battleId)
        {
            Battle battle = GetBattle(battleId);
            if (battle == null)
            {
                return false;
            }
            if (battle.HasPlayers())
            {
                throw new Exception("KickPlayerOut is required");
            }
            RemoveBattle(battle);
            return true;
        }

        private void RemoveBattle(Battle battle)
        {
            _battleMap.Remove(battle.id);
            _battles.Remove(battle);
            battle.Dispose();
            ClassPool.Put(battle);
        }

        private void InitModuleManager()
        {
            if (behaviacManager == null)
            {
                behaviacManager = BehaviacGlobal.Instance.CreateManager(-1);
            }
        }

        private void ShutdownModuleManager()
        {
            BehaviacGlobal.Instance.ReleaseManager(behaviacManager);
            behaviacManager = null;
        }

        public bool OnPlayerConnect(LogicPlayer logicPlayer, long battleId)
        {
            Battle battle = GetBattle(battleId);
            if (battle == null)
            {
                LogicLog.LogError($"找不到战斗 {battleId}");
                return false;
            }
            BattlePlayer battlePlayer = battle.InitPlayer(logicPlayer);
            if (battlePlayer == null)
            {
                LogicLog.LogError($"找不到玩家 {logicPlayer.id}");
                return false;
            }
            if (_battlePlayers.TryAdd(battlePlayer.id, battlePlayer) &&
                _sessionPlayers.TryAdd(logicPlayer.session, battlePlayer))
            {
                battle.EnterBattle(battlePlayer, logicPlayer.session); // 执行进入战斗

                // callback
                LogicHelper.OnPlayerEnterBattle?.Invoke(battlePlayer.id, battleId);
                return true;
            }
            LogicLog.LogError($"已经存在该玩家的战斗 {logicPlayer.id}");
            return false;
        }

        public bool KickPlayerOut(long pid, bool shutdownIfNoPlayer = true)
        {
            if (_battlePlayers.TryGetValue(pid, out BattlePlayer battlePlayer))
            {
                LeaveBattle(battlePlayer, shutdownIfNoPlayer);
                return true;
            }
            return false;
        }

        /** 离开关卡 */
        public void LeaveBattle(BattlePlayer player, bool shutdownIfNoPlayer = true)
        {
            // 执行战斗离开
            Battle battle = player.battle;
            battle.LeaveBattle(player);
            // 彻底删除 -- 该代码后不可继续访问player
            RemovePlayer(player);
            // 按理说，这里需要延迟删除 -- 外部可以主动触发立即删除
            if (shutdownIfNoPlayer && !battle.HasPlayers())
            {
                ShutdownBattle(battle.id);
            }
            // 感觉这个回调在Battle内执行更好
            LogicHelper.OnPlayerLeaveBattle?.Invoke(player.id, battle.id);
        }

        private void RemovePlayer(BattlePlayer player)
        {
            _battlePlayers.Remove(player.id);
            _sessionPlayers.Remove(player.session);
            ClassPool.Put(player);
        }

        public BattlePlayer GetBattlePlayer(ISession session)
        {
            if (_sessionPlayers.TryGetValue(session, out var player))
            {
                return player;
            }
            return null;
        }
    }
}